/* Copyright (c) FastLED. 2013-2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * @Author: soon.liao
 * @Date: 2024-08-31 15:24:11
 * @LastEditors: soon.liao
 * @LastEditTime: 2024-10-23 22:11:59
 * @FilePath: /src/application/ws63/rgb_master/rgbled/sence_fastled.cpp
 * @Description: NA
 * NA
 */
#include <stdio.h>
#include <unistd.h>

#include "pinctrl.h"
#include "soc_osal.h"
#include "app_init.h"
#include "sence.h"
#include "key_value.h"

#include <FastLED.h>

#ifdef __cplusplus
extern "C" {
#endif
#define SENCE_FASTLED_TASK_PRIO 17
#define DATA_PIN CONFIG_SPI_DO_MASTER_PIN
#define CLOCK_PIN CONFIG_SPI_CLK_MASTER_PIN
#define BRIGHTNESS_MAX 255
#define BRIGHTNESS_MIN 0
#define BRIGHTNESS_STEP 30
#define LED_MAX_NUM 254
#define BRIGHTNESS 64
#define LED_TYPE SK9822
#define COLOR_ORDER BGR
#define DEFAULT_LED_NUMBER 10
#define RGB_NUM 3
#define DELAY_POWER_UP_FASTLED_MS 3000
#define DELAY_SHOW_FASTLED_MS 20
#define DELAY_CLEAR_FASTLED_MS 500
#define DELAY_WAIT_SPI_INIT_MS 500

CRGB leds[LED_MAX_NUM];

#define UPDATES_PER_SECOND 10

static uint8_t g_cur_brightness = BRIGHTNESS;
static uint8_t g_cur_mode = 0;
static uint8_t g_cur_sence = 0;
static uint8_t g_cur_led_number = 0;
static bool g_cur_status = false;

CRGBPalette16 currentPalette;
TBlendType currentBlending;

CRGBPalette16 myRedWhiteBluePalette;

const TProgmemPalette16 myRedWhiteBluePalette_p = {CRGB::Red,
                                                   CRGB::Gray, // 'white' is too bright compared to red and blue
                                                   CRGB::Blue, CRGB::Black,

                                                   CRGB::Red,  CRGB::Gray,  CRGB::Blue,  CRGB::Black,

                                                   CRGB::Red,  CRGB::Red,   CRGB::Gray,  CRGB::Gray,
                                                   CRGB::Blue, CRGB::Blue,  CRGB::Black, CRGB::Black};

void change_strip_sence(uint8_t sence);
void change_strip_mode(uint8_t mode);
void get_nv_led_number(void)
{
    uint16_t kvalue_length;
    uint8_t kvalue;
    uint32_t errcode;
    errcode = key_value_rgb_master_led_number_read(&kvalue, &kvalue_length);
    osal_printk("\r\n key_value_rgb_master_led_number_read kvalue =%d kvalue_length=%d\r\n", kvalue, kvalue_length);
    if (errcode != 0) {
        osal_printk("\r\n key_value_rgb_master_led_number_read no success\r\n");
        g_cur_led_number = DEFAULT_LED_NUMBER;
    } else {
        g_cur_led_number = kvalue;
        osal_printk("\r\n key_value_rgb_master_led_number_read g_cur_led_number =%d\r\n", g_cur_led_number);
    }
}

void get_last_led_brightness(void)
{
    uint16_t kvalue_length;
    uint8_t kvalue;
    uint32_t errcode;
    errcode = key_value_rgb_master_led_brightness_read(&kvalue, &kvalue_length);
    osal_printk("\r\n key_value_rgb_master_led_brightness_read kvalue =%d kvalue_length=%d\r\n", kvalue, kvalue_length);
    if (errcode != 0) {
        osal_printk("\r\n key_value_rgb_master_led_brightness_read no success\r\n");
        g_cur_brightness = BRIGHTNESS;
    } else {
        g_cur_brightness = kvalue;
        osal_printk("\r\n key_value_rgb_master_led_brightness_read g_cur_brightness =%d\r\n", g_cur_brightness);
    }
}

void get_last_led_status(void)
{
    uint16_t kvalue_length;
    uint8_t kvalue;
    uint32_t errcode;
    errcode = key_value_rgb_master_on_off_read(&kvalue, &kvalue_length);
    osal_printk("\r\n key_value_rgb_master_on_off_read kvalue =%d kvalue_length=%d\r\n", kvalue, kvalue_length);
    if (errcode != 0) {
        osal_printk("\r\n key_value_rgb_master_on_off_read no success\r\n");
    } else {
        g_cur_status = kvalue;
        osal_printk("\r\n key_value_rgb_master_on_off_read g_cur_status =%d\r\n", g_cur_status);
    }
}
void get_last_mode_id(void)
{
    uint16_t kvalue_length;
    uint8_t kvalue;
    uint32_t errcode;
    errcode = key_value_rgb_master_mode_id_read(&kvalue, &kvalue_length);
    osal_printk("\r\n key_value_rgb_master_mode_id_read kvalue =%d kvalue_length=%d\r\n", kvalue, kvalue_length);
    if (errcode != 0) {
        osal_printk("\r\n key_value_rgb_master_mode_id_read no success\r\n");
    } else {
        g_cur_mode = kvalue;
        osal_printk("\r\n key_value_rgb_master_mode_id_read g_cur_mode =%d\r\n", g_cur_mode);
        change_strip_mode(g_cur_mode);
    }
}

void get_last_sence_id(void)
{
    uint16_t kvalue_length;
    uint8_t kvalue;
    uint32_t errcode;
    errcode = key_value_rgb_master_sence_id_read(&kvalue, &kvalue_length);
    osal_printk("\r\n key_value_rgb_master_sence_id_read kvalue =%d kvalue_length=%d\r\n", kvalue, kvalue_length);
    if (errcode != 0) {
        osal_printk("\r\n key_value_rgb_master_sence_id_read no success\r\n");
    } else {
        g_cur_sence = kvalue;
        osal_printk("\r\n key_value_rgb_master_sence_id_read g_cur_sence =%d\r\n", g_cur_sence);
        change_strip_sence(g_cur_sence);
    }
}

void fill_leds_from_palette_colors(uint8_t color_index)
{
    uint8_t brightness = g_cur_brightness;

    for (int i = 0; i < g_cur_led_number; ++i) {
        leds[i] = ColorFromPalette(currentPalette, color_index, brightness, currentBlending);
        color_index += RGB_NUM;
    }
}

void setup_totally_random_palette()
{
    int max_palette_entries = 16;
    for (int i = 0; i < max_palette_entries; ++i) {
        currentPalette[i] = CHSV(random8(), BRIGHTNESS_MAX, random8());
    }
}

// This function sets up a palette of black and white stripes,
// using code.  Since the palette is effectively an array of
// sixteen CRGB colors, the various fill_* functions can be used
// to set them up.
void setup_black_and_white_striped_palette()
{
    // 'black out' all 16 palette entries...
    fill_solid(currentPalette, 16, CRGB::Black);
    // and set every fourth one to white.
    uint8_t index = 0;
    uint8_t index_step = 4;
    currentPalette[index] = CRGB::White;
    index += index_step;
    currentPalette[index] = CRGB::White;
    index += index_step;
    currentPalette[index] = CRGB::White;
    index += index_step;
    currentPalette[index] = CRGB::White;
}

// This function sets up a palette of purple and green stripes.
void setup_purple_and_green_palette()
{
    CRGB purple = CHSV(HUE_PURPLE, 255, 255);
    CRGB green = CHSV(HUE_GREEN, 255, 255);
    CRGB black = CRGB::Black;

    currentPalette = CRGBPalette16(green, green, black, black, purple, purple, black, black, green, green, black, black,
                                   purple, purple, black, black);
}

void change_strip_mode(uint8_t mode)
{
    if (mode > MODE_INDEX_MAX) {
        mode = MODE_INDEX_0;
    }
    if (mode == MODE_INDEX_0) {
        currentBlending = NOBLEND;
    }
    if (mode == MODE_INDEX_1) {
        currentBlending = LINEARBLEND;
    }
    if (mode == MODE_INDEX_MAX) {
        currentBlending = LINEARBLEND_NOWRAP;
    }
    osal_printk("\r\n change_strip_mode mode=%d\r\n", mode);
    uint8_t g_mode_id = mode;
    int ret = key_value_rgb_master_mode_id_write(&g_mode_id, 1);
    osal_printk("\r\n%s key_value_rgb_master_mode_id_read val_int:%d ret: 0x%x\n", __FUNCTION__, g_mode_id, ret);
}

void change_strip_sence(uint8_t sence)
{
    if (sence == SENCE_INDEX_0) {
        currentPalette = CloudColors_p;
    }
    if (sence == SENCE_INDEX_1) {
        currentPalette = LavaColors_p;
    }
    if (sence == SENCE_INDEX_2) {
        currentPalette = OceanColors_p;
    }
    if (sence == SENCE_INDEX_3) {
        currentPalette = ForestColors_p;
    }
    if (sence == SENCE_INDEX_4) {
        currentPalette = RainbowColors_p;
    }
    if (sence == SENCE_INDEX_5) {
        currentPalette = PartyColors_p;
    }
    if (sence == SENCE_INDEX_6) {
        currentPalette = HeatColors_p;
    }
    if (sence == SENCE_INDEX_7) {
        currentPalette = RainbowStripeColors_p;
    }
    if (sence == SENCE_INDEX_8) {
        currentPalette = HeatColors_p;
    }
    if (sence == SENCE_INDEX_9) {
        setup_purple_and_green_palette();
    }
    if (sence == SENCE_INDEX_10) {
        setup_totally_random_palette();
    }
    if (sence == SENCE_INDEX_MAX) {
        setup_black_and_white_striped_palette();
    }
    osal_printk("\r\n change_strip_sence Sence=%d\r\n", sence);
    uint8_t g_sence_id = sence;
    int ret = key_value_rgb_master_sence_id_write(&g_sence_id, 1);
    osal_printk("\r\n%s key_value_rgb_master_sence_id_read val_int:%d ret: 0x%x\n", __FUNCTION__, g_sence_id, ret);
}

void rgb_led_strip_next_mode(void)
{
    if (g_cur_mode >= MODE_INDEX_MAX) {
        g_cur_mode = MODE_INDEX_0;
    } else {
        g_cur_mode++;
    }
    change_strip_mode(g_cur_mode);
    rgb_led_strip_set_on();
}

void rgb_led_strip_perv_mode(void)
{
    if (g_cur_mode > MODE_INDEX_0) {
        g_cur_mode--;
    } else {
        g_cur_mode = MODE_INDEX_MAX;
    }
    change_strip_mode(g_cur_mode);
    rgb_led_strip_set_on();
}

void rgb_led_strip_next_sence(void)
{
    if (g_cur_sence >= SENCE_INDEX_MAX) {
        g_cur_sence = SENCE_INDEX_0;
    } else {
        g_cur_sence++;
    }
    change_strip_sence(g_cur_sence);
    rgb_led_strip_set_on();
}

void rgb_led_strip_perv_sence(void)
{
    if (g_cur_sence <= SENCE_INDEX_0) {
        g_cur_sence = SENCE_INDEX_MAX;
    } else {
        g_cur_sence--;
    }
    change_strip_sence(g_cur_sence);
    rgb_led_strip_set_on();
}

// 设置LED条的亮度增加
void rgb_led_strip_add_brightness(void)
{
    if (g_cur_brightness >= BRIGHTNESS_MAX) {
        g_cur_brightness = BRIGHTNESS_MAX;
    } else {
        g_cur_brightness += BRIGHTNESS_STEP;
    }

    FastLED.setBrightness(g_cur_brightness);
    int ret = key_value_rgb_master_led_brightness_write(&g_cur_brightness, 1);
    osal_printk("\r\n%s key_value_rgb_master_led_brightness_write val_int:%d ret: 0x%x\n", __FUNCTION__,
                g_cur_brightness, ret);
}
// 设置LED条的亮度减少
void rgb_led_strip_min_brightness(void)
{
    if (g_cur_brightness <= BRIGHTNESS_MIN) {
        g_cur_brightness = BRIGHTNESS_MIN;
    } else {
        g_cur_brightness -= BRIGHTNESS_STEP;
    }
    FastLED.setBrightness(g_cur_brightness);
    int ret = key_value_rgb_master_led_brightness_write(&g_cur_brightness, 1);
    osal_printk("\r\n%s key_value_rgb_master_led_brightness_write val_int:%d ret: 0x%x\n", __FUNCTION__,
                g_cur_brightness, ret);
}

// 设置LED条的亮度
void rgb_led_strip_set_brightness(uint8_t value)
{
    if (value >= BRIGHTNESS_MIN && value <= BRIGHTNESS_MAX) {
        g_cur_brightness = value;
    } else {
        osal_printk("\r\n rgb_led_strip_set_brightness error value=%d\r\n", value);
        return;
    }
    FastLED.setBrightness(g_cur_brightness);
    int ret = key_value_rgb_master_led_brightness_write(&g_cur_brightness, 1);
    osal_printk("\r\n%s key_value_rgb_master_led_brightness_write val_int:%d ret: 0x%x\n", __FUNCTION__,
                g_cur_brightness, ret);
}

void rgb_led_strip_set_on(void)
{
    g_cur_status = LED_ON;
    uint8_t g_status = (uint8_t)g_cur_status;
    int ret = key_value_rgb_master_on_off_write(&g_status, 1);
    osal_printk("\r\n%s key_value_rgb_master_on_off_write val_int:%d ret: 0x%x\n", __FUNCTION__, g_status, ret);
}

void rgb_led_strip_set_off(void)
{
    g_cur_status = LED_OFF;
    uint8_t g_status = (uint8_t)g_cur_status;
    int ret = key_value_rgb_master_on_off_write(&g_status, 1);
    osal_printk("\r\n%s key_value_rgb_master_on_off_write val_int:%d ret: 0x%x\n", __FUNCTION__, g_status, ret);
}

void rgb_led_strip_toggle(void)
{
    g_cur_status = !g_cur_status;
    uint8_t g_status = (uint8_t)g_cur_status;
    int ret = key_value_rgb_master_on_off_write(&g_status, 1);
    osal_printk("\r\n%s key_value_rgb_master_on_off_write val_int:%d ret: 0x%x\n", __FUNCTION__, g_status, ret);
}

void setup()
{
    get_nv_led_number();
    get_last_led_brightness();
    get_last_led_status();
    get_last_mode_id();
    get_last_sence_id();
    osal_msleep(DELAY_POWER_UP_FASTLED_MS); // power-up safety delay
    FastLED.addLeds<LED_TYPE, DATA_PIN, CLOCK_PIN, COLOR_ORDER>(leds, g_cur_led_number).setCorrection(TypicalLEDStrip);
    FastLED.setBrightness(BRIGHTNESS);
}

void loop()
{
    static uint8_t start_index = 0;
    start_index = start_index + 1; /* motion speed */
    fill_leds_from_palette_colors(start_index);
    if (g_cur_status == 1) {
        FastLED.show();
        osal_msleep(DELAY_SHOW_FASTLED_MS);
    } else {
        FastLED.clear(true);
        osal_msleep(DELAY_CLEAR_FASTLED_MS);
    }
}

static void *fast_led_task(const char *arg)
{
    (void)arg;
    osal_printk("\r\n FastLEDDemo start\r\n");
    osal_msleep(DELAY_WAIT_SPI_INIT_MS); // 延迟spi初始化时间，系统有其他地方会修改到spi设定
    setup();

    while (1) {
        loop();
    }
    return NULL;
}

static void fast_led_entry(void)
{
    osal_task *task_handle = NULL;
    osal_kthread_lock();
    task_handle = osal_kthread_create((osal_kthread_handler)fast_led_task, 0, "fast_led_task", 0x4000);
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, SENCE_FASTLED_TASK_PRIO);
        osal_kfree(task_handle);
    }
    osal_kthread_unlock();
}
/* Run the i2c_slave_entry. */
app_run(fast_led_entry);

#ifdef __cplusplus
}
#endif